﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Text;
using System.Windows.Forms;
using DBSourceToolsLib.Interfaces;
using StructureMap;

namespace DBSourceToolsLib
{
    public class DatabaseProject
    {
        Hashtable m_ServerHash = new Hashtable();
        private string m_BaseDirectory;
        private string m_ProjectName;
        private string m_ProjectFileName;
        public string DefaultDatabaseName;
        private List<SourceDatabase> m_SourceDatabases = new List<SourceDatabase>();

        public bool ProjectNeedsWriteTargets { get; set; }

        public string BaseDirectory
        {
            get { return m_BaseDirectory; }
            set { 
                m_BaseDirectory = value;
                m_ProjectFileName = m_BaseDirectory + "\\" + this.ProjectName + ".xml";
                foreach (SourceDatabase oSourceDb in this.SourceDatabases)
                {
                    oSourceDb.BaseDirectory = m_BaseDirectory + "\\" + oSourceDb.NickName;
                }
            }
        }

        
        public string ProjectName
        {
            get { return m_ProjectName; }
            set { 
                // changing a project name effects it's base directory.
                m_ProjectName = value;
                m_ProjectFileName = m_BaseDirectory + "\\" + this.ProjectName + ".xml";
            }
        }

        

        public string ProjectFileName
        {
            get { return m_ProjectFileName; }
//            set { m_ProjectFileName = value; }
        }

        
        public List<SourceDatabase> SourceDatabases
        {
            get { return this.m_SourceDatabases; }
        }

        private DatabaseProject()
        {
        }

        public DatabaseProject(string strBaseDirectory, string strProjectName)
        {
            this.BaseDirectory = strBaseDirectory;
            this.ProjectName = strProjectName;
            
        }
        public DatabaseProject(string strProjectFileName)
        {
            this.BaseDirectory = strProjectFileName.Substring(0, strProjectFileName.LastIndexOf("\\"));
            m_ProjectFileName = strProjectFileName;
            this.ProjectName = this.ProjectFileName.Substring(this.BaseDirectory.Length + 1);
            this.ProjectName = this.ProjectName.Substring(0, this.ProjectName.LastIndexOf("."));
        }

        public void LoadFromDisk(bool bCreateScripts)
        {
            try
            {
                if (System.IO.File.Exists(this.ProjectFileName))
                {
                    DataSets.DatabasesDS oDatabasesDs = new DBSourceToolsLib.DataSets.DatabasesDS();
                    oDatabasesDs.ReadXml(this.ProjectFileName);
                    foreach (DataSets.DatabasesDS.DatabaseProjectRow oProjectRow in
                        oDatabasesDs.DatabaseProject.Rows)
                    {
                        this.DefaultDatabaseName = oProjectRow.DefaultDatabaseName;
                        foreach (DataSets.DatabasesDS.SourceDatabasesRow oSourceDatabaseRow in oProjectRow.GetSourceDatabasesRows())
                        {
                            SourceDatabase oSourceDatabase = null;
                            foreach (DataSets.DatabasesDS.DatabasesTableRow oDatabaseRow 
                                in oSourceDatabaseRow.GetDatabasesTableRows())
                            {
                                oSourceDatabase = new SourceDatabase(
                                    BuildDatabaseFromRow(oDatabaseRow, true, null), bCreateScripts);
                                m_SourceDatabases.Add(oSourceDatabase);

                            }
                            foreach (DataSets.DatabasesDS.DeploymentTargetsRow oDeploymentTargetRow in
                            oSourceDatabaseRow.GetDeploymentTargetsRows())
                            {
                                foreach (DataSets.DatabasesDS.DatabasesTableRow oDatabaseRow in
                                    oDeploymentTargetRow.GetDatabasesTableRows())
                                {
                                    oSourceDatabase.AddTargetDatabase(
                                        BuildDatabaseFromRow( oDatabaseRow, false, oSourceDatabase.BaseDirectory), false
                                        );
                                }
                            }

                            
                        }
                        
                    }
                }
                //else
                //{
                //    if (
                //        MessageBox.Show("The database configuration cannot be found", "Reload Configuration",
                //            MessageBoxButtons.YesNo) == DialogResult.Yes)
                //    {
                //        //
                //    }
                //    //throw new Exception("Cannot load Database Project file " + this.ProjectFileName);
                //}
            }
            catch (Exception ex)
            {
                throw new Exception("Cannot load Database Project file " + this.ProjectFileName
                    + Environment.NewLine + ex.ToString());
            }

        }

        private DBConnectionLib.Database BuildDatabaseFromRow(DataSets.DatabasesDS.DatabasesTableRow oDatabaseRow, bool isSourceDb, string sourceBaseDirectory)
        {
            
            // update BaseDirectory if the project has moved ?
            string projectBaseDirectory = UserSettings.Instance().ProjectBaseDirectory;
            string databaseBaseDirectory = oDatabaseRow.BaseDirectory;

            bool needsWriteTargets = false;

            if (!Directory.Exists(oDatabaseRow.BaseDirectory))
            {
                if (isSourceDb)
                    databaseBaseDirectory = projectBaseDirectory + @"\" + oDatabaseRow.NickName;
                else
                {
                    databaseBaseDirectory = sourceBaseDirectory + @"\DeploymentTargets\" + oDatabaseRow.NickName;
                    var test = "true";
                }
                UserSettings.Instance().ProjectNeedsSaving = true;
                needsWriteTargets = true;
            }

            DBConnectionLib.Database oDatabase
                = new DBConnectionLib.Database(oDatabaseRow.NickName, oDatabaseRow.ServerName,
                    oDatabaseRow.DatabaseName, databaseBaseDirectory);
            if (!oDatabaseRow.IsCommandSQLNull())
                oDatabase.CommandSQL = oDatabaseRow.CommandSQL;
            if (!oDatabaseRow.IsConnectionStringNull())
                oDatabase.ConnectionString = oDatabaseRow.ConnectionString;
            if (!oDatabaseRow.IsPasswordNull())
                oDatabase.Password = oDatabaseRow.Password;
            if (!oDatabaseRow.IsSQLVersionNull())
                oDatabase.SQLVersion = oDatabaseRow.SQLVersion;
            if (!oDatabaseRow.IsUserNameNull())
                oDatabase.UserName = oDatabaseRow.UserName;
            if (!oDatabaseRow.IsBaseDirectoryNull())
                oDatabase.BaseDirectory = databaseBaseDirectory;
            oDatabase.NeedsWriteTargets = needsWriteTargets;
            return oDatabase;
        }

        public void AddNewDatabase(DBConnectionLib.Database oDatabase)
        {
            // firstly override the base directory withe the project directory.
            oDatabase.BaseDirectory = this.BaseDirectory + "\\" + oDatabase.NickName;
            SourceDatabases.Add(new SourceDatabase(oDatabase, true));
        }

        public void CreateBaseDirectories()
        {
            if (! System.IO.Directory.Exists(this.BaseDirectory))
                System.IO.Directory.CreateDirectory(this.BaseDirectory);
            foreach (SourceDatabase oSource in this.SourceDatabases)
            {
                oSource.CreateMyDirectory();
                foreach (TargetDatabase oTarget in oSource.TargetDatabases)
                {
                    oTarget.CreateMyDirectory();
                }
            }

        }

        public void WriteProjectFile()
        {
            DataSets.DatabasesDS oDatabasesDs = new DBSourceToolsLib.DataSets.DatabasesDS();
            DataSets.DatabasesDS.DatabaseProjectRow oProjectRow =
                oDatabasesDs.DatabaseProject.AddDatabaseProjectRow(this.ProjectName);
            int iProjectID = oProjectRow.ProjectID;
                        
            foreach (SourceDatabase oSourceDb in this.SourceDatabases)
            {
                DataSets.DatabasesDS.DatabasesTableRow oDatabaseRow =
                    oDatabasesDs.DatabasesTable.NewDatabasesTableRow();

                int dbID = oDatabaseRow.DatabaseID;
                DatabaseToRow(oSourceDb, ref oDatabaseRow);

                oDatabasesDs.DatabasesTable.AddDatabasesTableRow(oDatabaseRow);

                DataSets.DatabasesDS.SourceDatabasesRow oSourceRow =
                    oDatabasesDs.SourceDatabases.AddSourceDatabasesRow(oProjectRow, dbID);

                foreach (TargetDatabase oTarget in oSourceDb.TargetDatabases)
                {
                    DataSets.DatabasesDS.DatabasesTableRow oTargetDatabaseRow =
                        oDatabasesDs.DatabasesTable.NewDatabasesTableRow();
                    int targetID = oTargetDatabaseRow.DatabaseID;
                    DatabaseToRow(oTarget, ref oTargetDatabaseRow);
                    oDatabasesDs.DatabasesTable.AddDatabasesTableRow(oTargetDatabaseRow);

                    DataSets.DatabasesDS.DeploymentTargetsRow oDepTarget =
                        oDatabasesDs.DeploymentTargets.NewDeploymentTargetsRow();
                    oDepTarget.SourceDatabaseID = dbID;
                    oDepTarget.TargetDatabaseID = targetID;

                    oDatabasesDs.DeploymentTargets.AddDeploymentTargetsRow(oDepTarget);
                }


            }

            oDatabasesDs.AcceptChanges();
            FileUtils.EnsureFileIsWriteable(this.ProjectFileName);
            oDatabasesDs.WriteXml(this.ProjectFileName);
        }

        private void DatabaseToRow(DBConnectionLib.Database oDatabase, ref DataSets.DatabasesDS.DatabasesTableRow oDatabaseRow)
        {
            oDatabaseRow.BaseDirectory = oDatabase.BaseDirectory;
            oDatabaseRow.CommandSQL = oDatabase.CommandSQL;
            oDatabaseRow.ConnectionString = oDatabase.ConnectionString;
            oDatabaseRow.DatabaseName = oDatabase.DatabaseName;
            oDatabaseRow.NickName = oDatabase.NickName;
            oDatabaseRow.Password = oDatabase.Password;
            oDatabaseRow.ServerName = oDatabase.DataSourceName;
            oDatabaseRow.UserName = oDatabase.UserName;

        }

        public DatabaseEx GetDatabaseEx(string strDatabaseNickName)
        {
            foreach (SourceDatabase oSource in SourceDatabases)
            {
                if (oSource.NickName.CompareTo(strDatabaseNickName) == 0)
                    return (DatabaseEx)oSource;
                else
                {
                    foreach (TargetDatabase oTarget in oSource.TargetDatabases)
                    {
                        if (oTarget.NickName.CompareTo(strDatabaseNickName)==0)
                            return (DatabaseEx)oTarget;
                    }
                }
            }
            return null;
        }

        public void DeleteDatabase(string strSourceDatabase)
        {
            int iFoundIndex = -1;
            for (int i = 0; i < this.SourceDatabases.Count; i++)
            {
                SourceDatabase oSource = this.SourceDatabases[i];
                if (strSourceDatabase.CompareTo(oSource.NickName) == 0)
                {
                    iFoundIndex = i;
                    break;
                }
            }

            if (iFoundIndex != -1)
                this.SourceDatabases.RemoveAt(iFoundIndex);

            if (this.SourceDatabases.Count > 0)
                UserSettings.Instance().CurrentDbNickName = this.SourceDatabases[0].NickName;
            else
                UserSettings.Instance().CurrentDbNickName = "";
            //SourceDatabase oSource = this.SourceDatabases[strSourceDatabase];
            //this.SourceDatabases.Remove(oSource);
        }

        public void CloseProject()
        {
            this.m_SourceDatabases.Clear();
        }

        private List<DBConnectionLib.Database> m_ServerList = new List<DBConnectionLib.Database>();
        public List<DBConnectionLib.Database> ServerList
        {
            get
            {
                return m_ServerList;
            }
        }

        public void LoadServerList()
        {
            foreach (DBSourceToolsLib.SourceDatabase oSource in SourceDatabases)
            {
                AddDatabase(oSource.DataSourceName, oSource.DatabaseName, oSource.NickName);
            }
            // add target databases
            foreach (DBSourceToolsLib.SourceDatabase oSource in SourceDatabases)
            {
                foreach (DBSourceToolsLib.TargetDatabase oTarget in oSource.TargetDatabases)
                {
                    AddDatabase(oTarget.DataSourceName, oTarget.DatabaseName, oTarget.NickName);
                }
            }
            foreach (string strKey in m_ServerHash.Keys)
            {
                List<DBListItem> dbList = (List<DBListItem>)m_ServerHash[strKey];
                foreach (DBListItem oDBListItem in dbList)
                {
                    ServerList.Add(DBSourceToolsLib.UserSettings.Instance().DatabaseProject.GetDatabaseEx(oDBListItem.NickName));
                }
            }

        }
        private void AddDatabase(string strServer, string strDatabase, string strNick)
        {
            string strKey = strServer;
            if (strKey.CompareTo(".") == 0)
            {
                strKey = "(local)";
            }
            if (m_ServerHash.ContainsKey(strKey))
            {
                List<DBListItem> dbList = (List<DBListItem>)m_ServerHash[strKey];
                bool bFound = false;
                foreach (DBListItem oItem in dbList)
                {
                    if ((oItem.Server.CompareTo(strServer) == 0)
                        && (oItem.Database.CompareTo(strDatabase) == 0))
                    {
                        bFound = true;
                        break;
                    }
                }
                if (!bFound)
                {
                    DBListItem oNewItem = new DBListItem(strServer, strDatabase, strNick);
                    dbList.Add(oNewItem);
                    m_ServerHash[strKey] = dbList;

                }
            }
            else
            {
                List<DBListItem> dbList = new List<DBListItem>();
                DBListItem oNewItem = new DBListItem(strServer, strDatabase, strNick);
                dbList.Add(oNewItem);
                m_ServerHash.Add(strKey, dbList);
            }

        }

        public List<string> ListDatabasesOnServer(DBConnectionLib.Database oDbServer)
        {
            List<string> dbList = new List<string>();
            Microsoft.SqlServer.Management.Smo.Server oServer = null;
                oServer = oDbServer.GetSmoServer();
            foreach (Microsoft.SqlServer.Management.Smo.Database oDb in oServer.Databases)
            {
                dbList.Add(oDb.Name);
            }
            return dbList;
        }
    }
    public class DBListItem
    {
        public DBListItem(string strServer, string strDb, string strNick)
        {
            Server = strServer;
            Database = strDb;
            NickName = strNick;
        }
        public string Server;
        public string Database;
        public string NickName;
    }

}
